package edu.princeton.cogsci.wn.search;

import java.util.Vector;

import edu.princeton.cogsci.wn.*;

/** This is a basic implementation of SearchMacro. It can be used as-is as a
 *  complete search and can be sub-classed to define narrower searches. 
 */
public class BasicSearch extends SearchMacro implements Constants
{
  protected int[] relationFilters = { ANY_RELATION };
  protected int depth = 1;

  protected static final int INFINITE = -1;

  public String getName( )
  {
    return "Complete Search";
  }

  public SearchResults doSearch( SearchParameters params )
  {
    SearchResults results = new SearchResults( params );
    results.setSearch( getName( ) );
    
    addForms( params, results.root );
    return results;
  }

  public void expandSearch( SearchResults.Node parent )
  {
    if ( parent.getData( ) instanceof SemanticRelation )
      addRelations( parent, 1 );
  }

  private void addForms( SearchParameters params, SearchResults.Node cursor )
  {
    cursor.addChild( params.word, true );
    addPoses( params, cursor.getLastChild( ) );
    
    if ( cursor.getLastChild( ).getChildCount( ) == 0 )
      cursor.removeChildAt( cursor.getChildCount( ) - 1 );

    /* If no sense was specified, perform morphology. */
    if ( params.sense == ALL_SENSES )
    {
      String[] forms = wni.lookupBaseForms( params.word );
      Vector formv = new Vector( );
      for ( int i = 0; i < forms.length; i++ )
      {
        if ( formv.contains( forms[i] ) )
          continue;
        params.word = forms[i];
        cursor.addChild( forms[i], true );
        addPoses( params, cursor.getLastChild( ) );
        if ( cursor.getLastChild( ).getChildCount( ) == 0 )
          cursor.removeChildAt( cursor.getChildCount( ) - 1 );
        else
          formv.add( forms[i] );
      }
    }
  }

  /** params.pos may be ANY_POS and params.sense may be ALL_SENSES. */

  private void addPoses( SearchParameters params, SearchResults.Node cursor )
  {
    if ( params.pos == ANY_POS )
    {
      for ( params.pos = NOUN; params.pos <= SATELLITE; params.pos++ )
      {
        cursor.addChild( posStrings[ params.pos ], true );
        addSynsets( params, cursor.getLastChild( ) );
        if ( cursor.getLastChild( ).getChildCount( ) == 0 )
          cursor.removeChildAt( cursor.getChildCount( ) - 1 );
      }
      params.pos = ANY_POS;
    }
    else
      addSynsets( params, cursor );
  }
  
  /** params.pos must not be ANY_POS, but params.sense may be ALL_SENSES. */

  private void addSynsets( SearchParameters params,
                           SearchResults.Node cursor )
  {
    Synset[] synsets;
    
    if ( params.sense == ALL_SENSES )
      synsets = wni.lookupSynsetAllSenses( params.word, params.pos );
    else
    {
      Synset synset = wni.lookupSynset( wni.lookupSynsetKey(
        params.word, params.pos, params.sense
      ) );
      if ( synset == null )
        synsets = new Synset[0];
      else
      {
        synsets = new Synset[1];
        synsets[0] = synset;
      }
    }
    
    for ( int i = 0; i < synsets.length; i++ )
    {
      cursor.addChild( synsets[i].getWordSense( params.word ), true );
      addRelations( cursor.getLastChild( ), this.depth );
    }
  }
  
  private void addRelations( SearchResults.Node cursor, int depth )
  {
    if ( depth == 0 )
      return;

    if ( depth == 1 )
      cursor.setExpanded( false );

    SemanticRelation[] relations;

    if ( cursor.getData( ) instanceof WordSense )
    {
      WordSense wordSense = ( WordSense )cursor.getData( );
      relations = wordSense.getSynset( ).getRelations( );
    }
    else if ( cursor.getData( ) instanceof SemanticRelation )
    {
      SemanticRelation relation = ( SemanticRelation )cursor.getData( );
      relations = wni.lookupSynset( relation.key ).getRelations( );
    }
    else return;

    for ( int i = 0; i < relations.length; i++ )
      for ( int j = 0; j < relationFilters.length; j++ )
        if ( relations[i].type == relationFilters[j] ||
             relationFilters[j] == ANY_RELATION )
        {
          cursor.addChild( relations[i], ( depth != 1 ) );
          addRelations( cursor.getLastChild( ), depth - 1 );
        }
  }

}
